【论文笔记】Everything Old is New Again: Binary Security of WebAssembly

2020-0722-Binary Security of WebAssembly

会议:USENIX SECURITY’20

论文名称:Everything Old is New Again: Binary Security of WebAssembly

链接:https://www.usenix.org/system/files/sec20-lehmann.pdf

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled.png


Introduction

WebAssembly是由W3C社区组开发的一项新技术。WebAssembly允许开发人员将他们本机的C/C++代码带到浏览器,代码由最终用户以接近本机的性能运行。WebAssembly已经在所有主流浏览器的最新版本中得到广泛支持,目前正在许多基于Web的服务中被使用。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%201.png

与native执行的程序相比,WebAssembly binary少了很多的防御措施,如Virtual memory, Stack canaries, Control-Flow Integrity (CFI)。WebAssembly社区也认为这些缓解措施不是必要的。

“Data execution prevention and stack smashing protection are not needed by WebAssembly programs.”
github.com/WebAssembly/design

“At worst, a buggy or exploited WebAssembly program can make a mess of the data in its own memory.”
Haas et al., PLDI 2017

作者针对WebAssembly的进行了深入研究,作者的主要贡献:

  • 对WebAssembly的线性内存、缓解机制进行了深度的分析,研究了由C、C++、Rust编译成的wasm的安全性比native binary低的原因。
  • 提供了一组攻击原语
  • 提供了三个应用中的攻击案例
  • 证明data、control-flow 攻击在WebAssembly中是可行的
  • 讨论了加固方案

Background on WebAssembly

Overview

  • wasm的可读形式称为wat
  • wasm一个module对应一个文件,包含functions、globals,以及一个linear memory和indirect call table
  • wasm bytecode是在一个基于栈的虚拟机上执行
  • wasm中的间接跳转、间接调用使用全局table中的index来表示,常量字符串使用其偏移来表示。
  • 目前主流的WebAssembly编译器Emscripten使用LLVM作为前端,fastcomp作为后端。在LLVM IR层面还未进行修改。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%202.png

相关介绍:

Type

WebAssembly中的globals、locals、arguments、函数返回值、instructions都规定了类型,binary在执行前会进行静态的类型检查。

WebAssembly中主要的四种基本类型:i32, i64, f32, f64。

WebAssembly中没有数组、records、designed pointers等复杂类型,在编译时会将这些source-level类型转为基本类型。

Control-Flow

WebAssembly中的branch只能在当前函数内跳转,多路branch指令也只能跳转到branch table中的目的地址。

memory中的data是不能作为bytecode来执行的。

在x86中的多种利用方式失效(injecting shellcode,使用indirect jump劫持控制流)

Indirect Calls

处理indirect call的方式:pop 出的操作数为called table的index,执行前会检查该函数的类型。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%203.png

关于间接调用

程序中共用一个跳转表,w2c_T0

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%204.png

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%205.png

Linear, Unmanaged Memory

WebAssembly提供一块linear menory,可以理解为一个全局数组,可以load、store其中已被分配的内存。用32-bit的pointer表示其中的地址,因此i32作为指针类型。

WebAssembly提供了自己的动态内存分配器,可以使用malloc、free,管理linear memory。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%206.png

线性内存的真实地址及其中的数据对WebAssembly是隐藏的。 换句话说,WebAssembly无法直接访问内存的内容。WebAssembly将在线性内存的索引处请求数据,浏览器负责确定实际地址。这是WebAssembly比C/C++等本地代码更安全的部分原因。

Host Environment

WebAssembly通过WebAssembly system interface (WASI)与外界交互,如I/O、file、network。

Security Analysis of Linear Memory

Managed vs. Unmanaged Data

Managed Data:local variables, global variables, values on the evaluation stack, and return addresses, reside in dedicated storage handled directly by the VM. WebAssembly 代码不能直接操作这些managed data,只能implicit使用。如local.get 0代表获取local 0,但并没有指针指向local 0。

Unmanaged Data:在linear memory中的所有data,可以由程序操作的。all non-scalar data,字符串,数组,列表,都在linear memory中,因为managed data都是没有地址的。

Memory Layout

The memory layout, i.e., the order of stack, heap, and data in linear memory, depends on the compiler.

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%207.png

Memory Protections

在native program中,最基础的内存保护机制为virtual memory with unmapped pages.

然而WebAssembly中的linear memory,都是被固定分配的,没有ASLR。

linear memory中的数据不能被作为代码执行,但是WebAssembly不允许标记内存为只读,因此linear memory中的所有数据都是可写的。

因此,WebAssembly中的常量并不是read only的,Heap、Stack、Data的位置都是固定的,且可能在两个段之间发生越界。

Attack Primitives

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%208.png

作者从三个方面进行介绍:

  • obtaining a write primitive
  • the data that can be overwritten
  • triggering security-compromising behavior by overwriting data

Obtaining a Write Primitive

  • Stack-based Buffer Overflow,覆盖栈上的其他变量
  • Stack Overflow,无stack overflow check的情况下,跨frame覆盖变量

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%209.png

  • Heap Metadata Corruption,Emscripten编译器可以让开发者自行选择分配器,默认的dlmalloc、可以减小程序体积的emmalloc、Rust可选择使用wee_alloc。
    • 作者发现emmalloc和wee_alloc的元数据都可能被corruption,emmalloc是一个first-fit的分配器

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2010.png

我自己尝试编译了wasm的程序,发现stack overflow checks在2019年七八月份已经被引入,且默认开启,关闭需手动将ASSERTIONS=0

1
emcc ./t.c -s ASSERTIONS=0 -s WASM=1 -o hello.html

Overwriting Data

  • Overwriting Stack Data
  • Overwriting Heap Data
  • Overwriting “Constant” Data

Triggering Unexpected Behavior

Redirecting Indirect Calls

攻击者可以修改indirect call的index来劫持控制流,但是WebAssembly提供了两套缓解机制:不是所有函数都在indirect call table中被声明;all calls(direct and indirect call)都会进行类型检查。

Code Injection into Host Environment

攻击者可以通过覆盖穿进eval的参数,来达到code injection的目的。

Application-specific Data Overwrite

有的WebAssembly程序被用于解释执行CIL/.NET语言,其中就会出现很多影响程序行为的机会。

End-to-End Attacks

作者举了三个攻击的例子。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2011.png

Cross-Site Scripting in Browsers

Stack-based buffer overflow (CVE-2018-14550)

攻击者覆盖img标签成script标签,以达到XSS的效果。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2012.png

Remote Code Execution in Node.js

  • 无CVE编号(可能自己编的例子)
  • emmalloc allocator

通过Heap metadata corruption,unsafe unlink时获得一次任意地址写的机会,修改Indirect call的function index来RCE。

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2013.png

Arbitrary File Write in Stand-alone VM

无CVE编号

通过Stack-based buffer overflow来覆盖String(在stack紧临着data的情况下),达到任意文件写

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2014.png

Quantitative Evaluation

实验对象:9 binaries from real-world, 17 C and C++ programs from SPEC CPU 2017 benchmark suite

Measuring Unmanaged Stack Usage

因为获得write primitive是攻击的入口,有多少数据被存储在unmanaged stack上呢?

由于WebAssembly中用全局变量来标识栈帧,没有x86中的rsp。作者使用启发式的方法来识别栈帧变量:在整个程序中被使用次数最多的即为栈帧变量。

WebAssembly function的prologues:

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2015.png

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2016.png

Measuring Indirect Calls and Targets

作者统计了测试集中的indirect call和indirectly callable function的数量、占比:(平均值为9.8%,49.2%)

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2017.png

Comparing with Existing CFI Policies

作者将WebAssembly中的间接调用类型检查与CFI进行了对比(上图、下图中数据所示)

用了两个指标来表示安全性:

  • The class count, i.e., how many different classes exist
  • The sizes of the classes, i.e., how many targets are in each class

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2018.png

Trying WebAssembly

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%207.png

Memory Layout

针对Figure 4中的a b c三个环境进行研究。

source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>

int aaa(int a, int b) {
int c;
printf("child val: %p\n", &c);
return a^b;
}

const char s1[] = "11111";
char s2[] = "s22222";

int main(int argc, char ** argv) {
// int a;
// scanf("%d", &a);
int *p = malloc(0x10);
printf("heap1: %p\n", p);
int *p2 = malloc(0x10);
printf("heap2: %p\n", p2);
int a,b,c;
printf("stack val: %p\n", &a);
printf("stack val: %p\n", &b);
printf("stack val: %p\n", &c);

aaa(1,2);

printf("s1: %p\n", s1);
printf("s2: %p\n", s2);

// printf("Hello World %d\n", aaa(1,2));
}

emcc

版本:

1
2
3
4
5
6
7
parallels@Ubuntu:~/webassembly$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.39.20
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 55fa315b0352b63454206600d6803fafacb42d5e)
...

parallels@Ubuntu:~/webassembly$ emcc ./t.c -s ASSERTIONS=0 -s WASM=1 -o hello.html
parallels@Ubuntu:~/webassembly$ emrun --no_browser --port 8080 .
1
2
3
4
5
6
7
8
heap1: 0x500ef8
heap2: 0x500f10
stack val: 0x500ebc
stack val: 0x500eb8
stack val: 0x500eb4
child val: 0x500e44
s1: 0x40f
s2: 0x678

clang

1
clang --target=wasm32-unknown-wasi --sysroot /home/parallels/webassembly/wasi-sdk-11.0/share/wasi-sysroot/ -O0 -g -o example.wasm t.c
1
2
3
4
5
6
7
8
9
parallels@Ubuntu:~/webassembly$ wasmtime ./example.wasm 
heap1: 0x20040
heap2: 0x20060
stack val: 0x1147c
stack val: 0x11478
stack val: 0x11474
child val: 0x11404
s1: 0x40f
s2: 0xd78

clang with stack-first

1
clang --target=wasm32-unknown-wasi --sysroot /home/parallels/webassembly/wasi-sdk-11.0/share/wasi-sysroot/ -O0 -Wl,--stack-first -g -o example_stack_first.wasm t.c
1
2
3
4
5
6
7
8
9
parallels@Ubuntu:~/webassembly$ wasmtime ./example_stack_first.wasm 
heap1: 0x20040
heap2: 0x20060
stack val: 0xffdc
stack val: 0xffd8
stack val: 0xffd4
child val: 0xff64
s1: 0x1000f
s2: 0x10978

Found something

  • 图a emcc中的两个差异
  • clang生成的wasm,其中的unused不超过0x10000,可能可以越界?
  • Heap都可能会被buffer overflow corrupt

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2019.png

Stack checks

  • 在emcc中已默认开启stack checks
  • clang中未默认开启

无符号wasm的逆向

wasm module类似静态链接的binary,各种库函数都杂在里面,无符号时可能会为逆向带来一定的难度。

Function

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2020.png

  • w2c_memory即为论文中提到的linear memory
  • store(memory, a2, a3)函数等价于 *(memory+a2) = a3
  • ret = load(memory, a2)函数等价于 ret = *(memory+a2)
  • scanf、printf的第一个参数就是data_segment中的offset
  • linear memory搜交叉引用可以发现其在init_memory函数中被初始化,使用memcpy将数个数组拷贝进去,所以说反编译出来的那几个数组即为linear memory,offset可以很容易的找到

Import functions

倒入函数的识别

可以通过从init函数中的init_table函数,或者是通过确认是printf等导入函数内的跟踪,找到一个全局变量T0,其为导入函数的table,所有导入的函数在其中被赋值,调用时也会通过该表进行间接函数调用。

因此,我们可以通过init_table函数中进行识别,恢复出部分与导入函数相关的function names

2020-0722-Binary%20Security%20of%20WebAssembly%2068b8e196fd154e2eaf9fccc8d94a6240/Untitled%2021.png

关于dlmalloc

WebAssembly默认使用dlmalloc,dlmalloc属于inplace metadata堆管理器。

所以,可能可以覆盖Heap中的metadata?

Related works